﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Physis.Security {

    public class UserAccount : Core.CoreConfigurationObject {
        
        #region Private Properties

        private Int64 securityAuthorityId = 0;

        private String distinguishedName = String.Empty;

        private String displayName = String.Empty;

        private DateTime? lastLogin = null;


        private List<Role> roles = null;

        private List<Role> enterpriseRoles = null;

        private List<String> enterprisePermissions = null; // EFFECTIVE PERMISSIONS FOR THE USER ACCOUNT 

        private List<Enterprise.Environment> environments = null;

        #endregion 


        #region Public Properties

        public Int64 SecurityAuthorityId { get { return securityAuthorityId; } set { securityAuthorityId = value; } }

        public String DistinguishedName { get { return distinguishedName; } set { distinguishedName = Data.Functions.SetValueMaxLength (value, 60); } }

        public String DisplayName { get { return displayName; } set { displayName = Data.Functions.SetValueMaxLength (value, 60); } }

        public DateTime? LastLogin { get { return lastLogin; } set { lastLogin = value; } }


        public List<Role> Roles {

            get {

                if (roles == null) {

                    roles = Application.Security.RolesByUserAccount (Id);

                    roles = (from currentRole in roles

                             orderby currentRole.EnvironmentName, currentRole.Name

                             select currentRole).ToList ();

                }

                return roles;

            }

            set {

                roles = value;

                foreach (Role currentRole in roles) {

                    currentRole.Application = Application;

                }

            }

        }

        public List<Role> EnterpriseRoles {

            get {

                if (enterpriseRoles == null) {

                    enterpriseRoles = Application.Security.RolesByUserAccount (Id);

                    enterpriseRoles = (from currentEnterpriseRole in enterpriseRoles

                                       where (currentEnterpriseRole.EnvironmentId == 0)

                                       orderby currentEnterpriseRole.Name

                                       select currentEnterpriseRole).ToList ();

                }

                return enterpriseRoles;

            }

        }

        public List<String> EnterprisePermissions {

            get {

                if (enterprisePermissions == null) { enterprisePermissions = Application.Security.PermissionsCalculatedForRoles ("Enterprise.", Roles).Select (current => current.Name).ToList(); }
                 
                return enterprisePermissions; 

            }
            
        }


        [System.Web.Script.Serialization.ScriptIgnore] [Newtonsoft.Json.JsonIgnore]
        public SecurityAuthority SecurityAuthority { get { return Application.Security.SecurityAuthority (securityAuthorityId, true); } }

        public String SecurityAuthorityName { get { return ((SecurityAuthority != null) ? SecurityAuthority.Name : String.Empty); } }


        [System.Web.Script.Serialization.ScriptIgnore] [Newtonsoft.Json.JsonIgnore]
        public List<Enterprise.Environment> Environments {

            get {

                if (environments == null) { environments = Application.Enterprise.EnvironmentsByUserAccountId (Id); }

                return environments;

            }

        }

        #endregion 

        
        #region Constructors

        public UserAccount (Application forApplication) : base (forApplication) {

            return;

        }

        public UserAccount (Application forApplication, Int64 forId) : base (forApplication) {

            if (!Load (forId, Application.EnterpriseDatabase)) {

                throw new Exception (GetType ().FullName + ".Load [Failed]: " + forId);

            }

            return;

        }

        public UserAccount (Application forApplication, Int64 forSecurityAuthorityId, String forName) : base (forApplication) {

            String selectStatement = String.Empty;
            
            selectStatement += "SELECT * FROM " + ObjectType;

            selectStatement += "  WHERE SecurityAuthorityId = " + forSecurityAuthorityId;

            selectStatement += "    AND Name = '" + forName.Replace ("'", "''") + "'";

            if (!LoadFromDatabase (selectStatement, Application.EnterpriseDatabase)) {

                throw new Exception (GetType ().FullName + ".Load [Failed]: " + forSecurityAuthorityId + "." + forName);

            }

            return;

        }

        #endregion


        #region Database Functions 

        protected override Boolean LoadChildObjects () {

            //environments = new List<Enterprise.Environment> ();

            //String selectStatement = "SELECT * FROM UserAccountEnvironment WHERE UserAccountId = " + Id;

            //System.Data.DataTable userAccountEnvironmentTable = Application.EnterpriseDatabase.SelectDataTable (selectStatement);

            //foreach (System.Data.DataRow currentRow in userAccountEnvironmentTable.Rows) {

            //    Enterprise.Environment environment = Application.Enterprise.Environment (Data.Readers.DataReader.Int64FromDatabase (currentRow, "EnvironmentId"), true);

            //    environments.Add (environment);

            //}

            return true;

        }

        public override void MapDataFields (System.Data.DataRow currentRow) {

            base.MapDataFields (currentRow);

            securityAuthorityId = Data.Readers.DataReader.Int64FromDatabase (currentRow, "SecurityAuthorityId");

            distinguishedName = Data.Readers.DataReader.StringFromDatabase (currentRow, "DistinguishedName");

            displayName = Data.Readers.DataReader.StringFromDatabase (currentRow, "DisplayName");

            lastLogin = Data.Readers.DataReader.DateTimeFromDatabaseAllowNull (currentRow, "LastLogin");

            return;

        }

        public override Boolean Save () {

            Boolean success = false;

            String deleteStatement; // RESERVED FOR FUTURE USE - ROLE MEMBERSHIP DELETES/UPDATES 

            try {

                Dictionary<String, String> validationErrors = Validate ();

                if (validationErrors.Count > 0) {

                    Application.SetLastException (new Exception ("Invalid [" + validationErrors.Keys.First () + "]: " + validationErrors.Values.First ()));

                    return false;

                }

                UpdateModifiedToSessionUser ();


                Application.EnterpriseDatabase.BeginTransaction ();

                System.Data.IDbCommand command = Application.EnterpriseDatabase.CreateCommand (ObjectType + "_InsertUpdate");

                command.CommandType = System.Data.CommandType.StoredProcedure;


                Application.EnterpriseDatabase.AppendCommandParameter (command, "Id", Id);

                Application.EnterpriseDatabase.AppendCommandParameter (command, "Name", Name, 60);

                Application.EnterpriseDatabase.AppendCommandParameter (command, "Description", Description, 999);


                Application.EnterpriseDatabase.AppendCommandParameter (command, "SecurityAuthorityId", SecurityAuthorityId);

                Application.EnterpriseDatabase.AppendCommandParameter (command, "DistinguishedName", DistinguishedName, 999);

                Application.EnterpriseDatabase.AppendCommandParameter (command, "DisplayName", DisplayName, 999);


                Application.EnterpriseDatabase.AppendCommandParameter (command, "ModifiedId", Application.Session.UserAccountName, 60);

                success = (command.ExecuteNonQuery () > 0);

                if (success) { SetIdentity (Application.EnterpriseDatabase); }


                // UPDATE ROLE MEMBERSHIP IF WE WANT TO ALLOW HERE AS CHILD OBJECTS 

                deleteStatement = "DELETE FROM RoleMembership WHERE UserAccountId = " + Id;

                success = Application.EnterpriseDatabase.ExecuteSqlStatement (deleteStatement);

                if (!success) { throw Application.EnterpriseDatabase.LastException; }


                foreach (Role currentRole in roles) {

                    RoleMembership roleMembership = new RoleMembership (Application);

                    roleMembership.RoleId = currentRole.Id;

                    roleMembership.UserAccountId = Id;

                    success = success && (roleMembership.Save ());

                    if (!success) { throw Application.EnterpriseDatabase.LastException; }

                }
                

                Application.EnterpriseDatabase.CommitTransaction ();

            }

            catch (Exception exception) {

                Application.EnterpriseDatabase.RollbackTransaction ();

                success = false;

                Application.SetLastException (exception);

            }

            return success;

        }

        public override Boolean Delete () {

            Boolean success = false;

            String deleteStatement;

            try {

                Application.EnterpriseDatabase.BeginTransaction ();


                // DELETE CHILD RECORDS IN THE RELATIONSHIP 

                deleteStatement = "DELETE FROM RoleMembership WHERE UserAccountId = " + Id;

                success = Application.EnterpriseDatabase.ExecuteSqlStatement (deleteStatement);

                if (!success) { throw Application.EnterpriseDatabase.LastException; }


                success = success && base.DeleteFromDatabase (DeleteStatementById (), Application.EnterpriseDatabase);

                if (!success) { throw Application.EnterpriseDatabase.LastException; }


                Application.EnterpriseDatabase.CommitTransaction ();

            }

            catch (Exception exception) {

                Application.EnterpriseDatabase.RollbackTransaction ();

                success = false;

                Application.SetLastException (exception);

            }

            return success;


        }

        #endregion 


        #region Public Methods

        public Boolean HasEnvironmentAccess (Int64 environmentId) {

            // TODO: ENVIRONMENT ACCESS 

            return false;

        }

        public void UpdateLastLogin () {

            lastLogin = DateTime.Now;

            String updateStatement = "UPDATE UserAccount SET LastLogin = '" + lastLogin.Value.ToString ("MM/dd/yyyy hh:mm:ss") + "' WHERE Id = " + Id;

            if (!Application.EnterpriseDatabase.ExecuteSqlStatement (updateStatement)) {

                Application.SetLastException (Application.EnterpriseDatabase.LastException);

            }

            return;

        }

        #endregion 

    }

}
